May 15, 2021
목차
1-1. 로그인 페이지 마크다운 및 로그인 유효성 검사
1-2. 로그인, 회원가입 기능
2-1. 메인페이지 컴포넌트 분리
2-2-1. 메인페이지 Nav.js
2-2-2. 메인페이지 Feed.js
2-3-1. 메인페이지 Article.js
저번주에 자바스크립트로 인스타그램 로그인 페이지와 메인페이지를 구현했는데 이번주에는 리액트로 구현해보았다 !
(1) state 만들기
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
id: '',
pw: '',
};
}
(2) 입력한 내용 input에 반영
onChangeInput = e => {
const { name, value } = e.target
this.setState({
[name]: value,
})
}
(3) 유효성 검사
render() {
const validation = this.state.id.includes('@') && this.state.pw.length > 4;
return (
<div className="LoginJY">
<main className="LoginBox">
<h1 className="title">Westagram</h1>
<div className="login">
<input
className="loginInputs loginId"
name="id"
value={this.state.id}
onChange={this.onChangeInput}
type="text"
placeholder="전화번호, 사용자 이름 또는 이메일"
/>
<input
className="loginInputs loginPw"
name="pw"
value={this.state.pw}
onChange={this.onChangeInput}
type="password"
placeholder="비밀번호"
/>
</div>
<button
className={`loginBtn ${validation ? 'blueBackgroundColor' : null}`}
onClick={this.onClickLoginBtn}
{/*------------------------------------ */}
{/* 위 onClickLoginBtn 함수는 아래에서 설명! */}
{/*------------------------------------ */}
>
로그인
</button>
백엔드와 처음 협업해보았다 ! 현재 만든 버튼이 로그인버튼 하나이기 때문에 fetch의 API를 login 과 signup 으로 직접 바꿔가면서 실행했다. id, pw를 입력하면 백엔드분 화면에서 바로바로 떴는데 신기했다 😃
onClickLoginBtn = () => {
fetch('http://10.58.7.181:8000/user/login', {
method: 'POST',
body: JSON.stringify({
email: this.state.id,
password: this.state.pw,
}),
})
.then(response => response.json())
.then(result => {
if (result.message === 'SUCCESS') {
this.props.history.push('/mainjy')
localStorage.setItem('access-token', result.ACCESS_TOKEN)
}
})
}
자바스크립트로 장황하게 쓰여있던 코드들을 컴포넌트로 나름 쪼개보았다 !
(1) Nav.js
<Link>
컴포넌트 사용과 withRouterHOC
의 두가지 방법이 있다.import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import './Nav.scss'
class Nav extends Component {
constructor() {
super()
this.state = {
navLink: [
{
id: 1,
imgAlt: '추천하는 사람들',
imgSrc:
'https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/explore.png',
},
{
id: 2,
imgAlt: '새롭게 나를 팔로우한 사람들',
imgSrc:
'https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/heart.png',
},
{
id: 3,
imgAlt: '내 계정',
imgSrc:
'https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/bearu/profile.png',
},
],
}
}
goToMain = () => {
this.props.history.push('/loginjy')
}
render() {
return (
<nav className="Nav">
<a className="navLogo" href="main.html">
<img alt="위스타그램 로고" src="/images/leejiyon/instagram.png" />
<div className="navLogoTitle">Westagram</div>
</a>
<div className="navSearchWrap">
<input className="navSearch" type="text" placeholder="검색" />
</div>
<ul className="navLink">
{this.state.navLink.map(link => (
<li key={link.id}>
<img alt={link.imgAlt} src={link.imgSrc} />
</li>
))}
</ul>
</nav>
)
}
}
export default withRouter(Nav)
(2) Feed.js
[
{
"header": {
"headerId": "jiyon",
"alt": "하오 이미지",
"className": "profileImg",
"src": "/images/leejiyon/hao.jpg"
},
"articleImg": {
"alt": "산 정상",
"className": "articleImg",
"src": "images/leejiyon/nature.jpg"
},
"myComment": "백두산 정상"
},
{
"header": {
"headerId": "dongseok",
"alt": "전동석 이미지",
"className": "profileImg",
"src": "/images/leejiyon/dongseok.jpg"
},
"articleImg": {
"alt": "아이유",
"className": "articleImg",
"src": "images/leejiyon/iu.jpg"
},
"myComment": "아이유 안녕"
}
]
class Feeds extends Component {
state = {
articleList: [],
};
componentDidMount() {
fetch('http://localhost:3000/data/leejiyon/articledata.json', {
method: 'GET',
})
.then(res => res.json())
.then(articleData => {
this.setState({
articleList: articleData,
});
});
}
(3-1) Article.js - 데이터 키값 id 를 ref로 사용
componentDidMount() {
this.idRef.current = 4;
}
componentDidUpdate(prevProps, prevState) {
if (prevState.userComments.length !== this.state.userComments.length) {
this.idRef.current += 1;
}
}
onClickPostBtn = () => {
if (!this.state.commentInputValue.length) {
return;
}
this.setState({
userComments: [
...this.state.userComments,
{
id: this.idRef.current,
userId: this.state.currentUser,
comment: this.state.commentInputValue,
showTrashBtn: true,
},
],
commentInputValue: '',
});
this.inputRef.current.focus();
};
(3-2) Article.js - 댓글 각각 좋아요 삭제 누르기
// ------------ 현재 userComments의 구조 --------------
userComments: [
{
id: 1,
userId: 'iu',
comment: '아자아자',
showTrashBtn: false,
liked: true,
},
{
id: 2,
userId: 'jiyon',
comment: '공부를 하자',
showTrashBtn: false,
liked: false,
},
{
id: 3,
userId: 'Shaman_king',
comment: '벨로그 쓰기',
showTrashBtn: false,
liked: false,
},
// ------------ 처음 코드 -------------
onClickCommentHeartBtn = id => {
this.state.userComments.filter((comment, i) => {
const userComments = [...this.state.userComments];
if (comment.id === id) {
comment.liked = !comment.liked;
userComments[i] = comment;
this.setState({
userComments: userComments,
});
}
};
// ------------ 수정한 코드 -------------
onClickCommentHeartBtn = id => {
this.setState({
userComments: this.state.userComments.map(comment =>
comment.id === id ? { ...comment, liked: !comment.liked } : comment
),
})
}
onClickDeleteBtn = clickedComment => {
this.setState({
userComments: this.state.userComments.filter(
user => user.id !== clickedComment.id
),
})
}